package com.bolt.support.script;

import com.bolt.common.utils.StrUtil;
import com.bolt.convention.exception.ScriptRuntimeException;

import javax.script.*;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by Administrator on 2018/5/30.
 */
public class ScriptUtil {
    /**
     * 获得 {@link ScriptEngine} 实例
     *
     * @param name 脚本名称
     * @return {@link ScriptEngine} 实例
     */
    public static ScriptEngine getScript(String name) {
        return new ScriptEngineManager().getEngineByName(name);
    }

    /**
     * 获得 Javascript引擎 {@link JavaScriptEngine}
     *
     * @return {@link JavaScriptEngine}
     */
    public static JavaScriptEngine getJavaScriptEngine() {
        return new JavaScriptEngine();
    }

    /**
     * 编译脚本
     *
     * @param script 脚本内容
     * @return {@link CompiledScript}
     * @throws ScriptRuntimeException 脚本异常
     * @since 3.2.0
     */
    public static Object eval(String script) throws ScriptRuntimeException {
        try {
            return compile(script).eval();
        } catch (ScriptException e) {
            throw new ScriptRuntimeException(e);
        }
    }

    /**
     * 编译脚本
     *
     * @param script  脚本内容
     * @param context 脚本上下文
     * @return {@link CompiledScript}
     * @throws ScriptRuntimeException 脚本异常
     * @since 3.2.0
     */
    public static Object eval(String script, ScriptContext context) throws ScriptRuntimeException {
        try {
            return compile(script).eval(context);
        } catch (ScriptException e) {
            throw new ScriptRuntimeException(e);
        }
    }

    /**
     * 编译脚本
     *
     * @param script   脚本内容
     * @param bindings 绑定的参数
     * @return {@link CompiledScript}
     * @throws ScriptRuntimeException 脚本异常
     * @since 3.2.0
     */
    public static Object eval(String script, Bindings bindings) throws ScriptRuntimeException {
        try {
            return compile(script).eval(bindings);
        } catch (ScriptException e) {
            throw new ScriptRuntimeException(e);
        }
    }

    /**
     * 编译脚本
     *
     * @param script 脚本内容
     * @return {@link CompiledScript}
     * @throws ScriptRuntimeException 脚本异常
     * @since 3.2.0
     */
    public static CompiledScript compile(String script) throws ScriptRuntimeException {
        try {
            return compile(getJavaScriptEngine(), script);
        } catch (ScriptException e) {
            throw new ScriptRuntimeException(e);
        }
    }

    /**
     * 编译脚本
     *
     * @param engine 引擎
     * @param script 脚本内容
     * @return {@link CompiledScript}
     * @throws ScriptException 脚本异常
     */
    public static CompiledScript compile(ScriptEngine engine, String script) throws ScriptException {
        if (engine instanceof Compilable) {
            Compilable compEngine = (Compilable) engine;
            return compEngine.compile(script);
        }
        return null;
    }

    public static Object invokeFunction(CompiledScript engine,String functionName,Object[] args){
        try {
            engine.eval();
            Invocable invoke = (Invocable) engine.getEngine();
            Object result = invoke.invokeFunction(functionName, args);
            return result;
        } catch (Exception e) {
            throw new ScriptRuntimeException(e);
        }
    }

    public static String[] getParamNames(String script) {
        Pattern pattern = Pattern.compile("function\\s.*?\\(([^)]*)\\)");
        Matcher matcher = pattern.matcher(script);
        String args = matcher.find() ? matcher.group(1) : "";
        return StrUtil.split(args, ',').stream()
                .map(k -> k.replace("\\/\\*.*\\*\\/", "").trim())
                .toArray(size -> new String[size]);
    }

    public static String getFunctionName(String script){
        Pattern pattern = Pattern.compile("^function\\s+([\\w\\$]+)\\s*\\(");
        Matcher matcher = pattern.matcher(script);
        return matcher.find() ? matcher.group(1) : "";
    }


    public static void main(String[] arg) {
        String script$function = "function num (x,y) { return x*y }";

        CompiledScript  engine = ScriptUtil.compile(script$function);

        String[] params = ScriptUtil.getParamNames(script$function);

        Arrays.stream(params).forEach(System.out::println);

        String name = ScriptUtil.getFunctionName(script$function);

        System.out.println("name="+name);

        try {
            engine.eval();
            Invocable invoke = (Invocable) engine.getEngine();
            Object result = invoke.invokeFunction(name, 8,8);
            System.out.println("result="+result);
        } catch (Exception e) {
            throw new ScriptRuntimeException(e);
        }




    }
}
